import {
  sequelize,
  Progress,
  Answer,
  Comment,
  Document,
  Task,
  User
} from '../models'
import { sendNotification } from './notification'
import EVENTS from '../events'

const getTargetScope = ({
  answerId,
  progressId,
  documentId,
  commentId,
  taskId
}) => {
  let result = {}
  if (taskId) {
    result.targetId = taskId
    result.scope = 'task'
  }
  if (answerId) {
    result.targetId = answerId
    result.scope = 'answer'
  }
  if (progressId) {
    result.targetId = progressId
    result.scope = 'progress'
  }
  if (commentId) {
    result.targetId = commentId
    result.scope = 'comment'
  }
  if (documentId) {
    result.targetId = documentId
    result.scope = 'document'
  }
  return result
}

/**
 * Create an comment for answer/progress
 * @param {object} input: { answerId, progressId, taskId, documentId, commentId, userId, data, liked, rating }
 * @return {object} Comment
 */
export const createOrUpdate = async ({
  answerId,
  progressId,
  commentId,
  taskId,
  documentId,
  userId,
  data,
  liked = 0,
  isNeedRedo = false,
  rating
}) => {
  return sequelize.transaction(async t => {
    let target = null
    let targetScope = getTargetScope({
      answerId,
      progressId,
      documentId,
      commentId,
      taskId
    })

    if (taskId) {
      target = await Task.findById(taskId, { transaction: t })
    }
    if (answerId) {
      target = await Answer.findById(answerId, { transaction: t })
    }
    if (progressId) {
      target = await Progress.findById(progressId, { transaction: t })
    }
    if (commentId) {
      target = await Comment.findById(commentId, { transaction: t })
    }
    if (documentId) {
      target = await Document.findById(documentId, { transaction: t })
    }
    if (target == null) {
      throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}`)
    }
    let comment = await Comment.findOne(
      {
        where: {
          targetId: targetScope.targetId,
          scope: targetScope.scope,
          userId,
          hasData: data != null
        }
      },
      { transaction: t }
    )
    let user = await User.findById(userId)
    if (!user) {
      throw new Error(`${ERRORS.DBERR.ERR_UNAUTHORIZED_OPERATION}`)
    }
    let userInfo = {
      id: user.id,
      nickName: user.nickName,
      avatarUrl: user.avatarUrl
    }
    let commentLiked = 0
    if (comment == null || data) {
      comment = await Comment.create(
        {
          data,
          userId,
          targetId: targetScope.targetId,
          scope: targetScope.scope,
          liked,
          rating,
          userInfo
        },
        { transaction: t }
      )
    } else {
      commentLiked = comment.liked
      comment.liked = liked
      comment.rating = rating
      comment.userInfo = userInfo
      await comment.save({ transaction: t })
    }
    let silent = false
    if (liked !== 0 && liked !== commentLiked) {
      target.likeCount += liked > 0 ? 1 : -1
      silent = true
    }
    // For answers and progresses
    // If userId is task owner, update target rating
    if (target.taskId) {
      if (rating || data) {
        let task = await Task.findById(target.taskId)
        if (task && task.ownerId == userId) {
          target.rating = rating || target.rating
          target.state = 'check'
          target.unreadCount = -1
          // update progress state if target is an answer
          if (target.progressId) {
            let progress = await Progress.findById(target.progressId, {
              transaction: t
            })
            if (progress) {
              progress.unreadCount = -1
              progress.state = 'check'
              await progress.save({ transaction: t })
              EVENTS.emit('progress-comment-after', {versionId: progress.versionId, progressId: progress.id, data, isNeedRedo})
            }
          }
        }
      }
    }
    await target.save({ transaction: t, silent })

    return comment
  })
}

/**
 * Remove an existing comment
 * @param {int} commentId
 * @param {int} userId
 * @return {object} Comment
 */
export const removeRating = async ({
  answerId,
  progressId,
  commentId,
  taskId,
  documentId,
  userId
}) => {
  return sequelize.transaction(async t => {
    let target = null
    let targetScope = getTargetScope({
      answerId,
      progressId,
      documentId,
      commentId,
      taskId
    })

    if (taskId) {
      target = await Task.findById(taskId, { transaction: t })
    }
    if (answerId) {
      target = await Answer.findById(answerId, { transaction: t })
    }
    if (progressId) {
      target = await Progress.findById(progressId, { transaction: t })
    }
    if (commentId) {
      target = await Comment.findById(commentId, { transaction: t })
    }
    if (documentId) {
      target = await Document.findById(documentId, { transaction: t })
    }
    if (target == null) {
      throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}`)
    }
    let comment = await Comment.findOne(
      {
        where: {
          targetId: targetScope.targetId,
          scope: targetScope.scope,
          hasData: false,
          userId
        }
      },
      { transaction: t }
    )
    if (comment) {
      if (comment.liked > 0) {
        target.likeCount -= 1
        target.save({ transaction: t })
      }
      await comment.destroy({ transaction: t })
    }

    return comment
  })
}

/**
 * Remove an existing rating
 * @param {int} commentId
 * @param {int} userId
 * @return {object} Comment
 */
export const remove = async (commentId, userId) => {
  let comment = await Comment.findById(commentId)
  if (comment == null) {
    throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}`)
  }
  if (userId && comment.userId != userId) {
    throw new Error(`${ERRORS.DBERR.ERR_UNAUTHORIZED_OPERATION}`)
  }
  return await comment.destroy()
}

/**
 * 批量回复
 * @param  {Int}  userId  评论者用户ID
 * @param  {Int}  taskId  任务ID
 * @param  {[Int]}  userIds 回复对像
 * @param  {[Json]}  data    数据
 * @return {Promise}         [description]
 */
export const createBulkForTask = async ({userId, taskId, userIds, data, isNeedRedo = true}) => {
  let task = await Task.findById(taskId)
  if (!task) throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}`)
  if (task.ownerId != userId) throw new Error(`${ERRORS.DBERR.ERR_UNAUTHORIZED_OPERATION}`)

  let answers = await Answer.findAll({
    where: {taskId, userId: userIds},
    order: [['id', 'desc']],
    raw: true
  })
  if (isNeedRedo) {
    await Progress.update({
      state: 'redo'
    }, {
      where: {
        taskId,
        userId: userIds
      }
    })
  }
  let kvUserIdAnswerId = {}
  answers.forEach(n => {
    if (kvUserIdAnswerId[n.userId]) return
    kvUserIdAnswerId[n.userId] = n.id
  })
  Object.keys(kvUserIdAnswerId).forEach(uid => {
    let answerId = kvUserIdAnswerId[uid]
    createOrUpdate({
      answerId,
      userId,
      data
    }).catch(e => {
      console.error(e)
    })
  })
  return task
}
